﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using LastoRevival.Service.Contracts;
using LastoRevival.Service.Implementations.Exceptions;
using LastoRevival.Service.Contracts.Models;
using LastoRevival.Service.Implementations.Data;
using LastoRevival.ForumCode;

namespace LastoRevival.Service.Implementations
{
	public class PostService : ServiceBase, IPostService
	{
		BBCodeParserExtension parser;
		UserService userService;
		IList<BBTagReference> forumCodes;


		private IDataProvider DataProvider
		{
			get
			{
				return DependencyFactory.GetDataProvider();
			}
		}

		#region helpers

		private void VerifyEditPermission(UserProfile currentUser, EditPostModel post)
		{

			if (!CanEdit(currentUser, post))
			{
				throw new NoAccessException();
			}
		}

		private bool CanEdit(UserProfile currentUser, PostModelBase post)
		{
			var allowed = false;

			// permitted if the user is editing his own post
			if (currentUser.Details.UserID == post.UserID)
			{
				allowed = true;
			}

			// not permitted if the post is locked
			if (post.Locked)
			{
				allowed = false;
			}

			// always permitted if the user is an admistrator or moderator
			if (currentUser.Details.IsAdministrator || currentUser.CanModerate(post.ForumID))
			{
				allowed = true;
			}

			return allowed;
		}
		#endregion

		public PostService(BBCodeParserExtension parser, UserService userService, IList<BBTagReference> forumCodes)
		{
			this.parser = parser;
			this.userService = userService;
			this.forumCodes = forumCodes;
		}

		public int CreatePost(Guid loginToken, CreatePostModel post)
		{
			UpdateSession(loginToken);

			var profile = userService.GetActiveProfile(loginToken);

			var allowed = profile.CanCreateThread(post.ForumID);

			if (!allowed)
			{
				throw new NoAccessException();
			}
			
			// todo: validation on service layer
			var userID = DataProvider.GetProfile(loginToken).UserID;
			var postID = DataProvider.CreatePost(userID, post);

			return postID;
		}

		public ViewPostModel GetPostForViewing(Guid loginToken, int postID)
		{
			UpdateSession(loginToken);

			var profile = userService.GetActiveProfile(loginToken);

			var post = DataProvider.GetPostForViewing(postID);

			// only moderators can view locked posts
			if (post.Locked && !profile.CanModerate(post.ForumID))
			{
				throw new NoAccessException();
			}


			// decode bbcode
			// Transform the supplied text (Markdown) into HTML.
			// store the un-parsed value so we can pass it down too
			post.BodyUnformatted = post.Body;
			post.Body = parser.ToHtml(post.Body);
			post.Signature = parser.ToHtml(post.Signature);

			post.CanEdit = CanEdit(profile, post);

			return post;
		}

		public EditPostModel GetPostForEditing(Guid loginToken, int postID)
		{
			UpdateSession(loginToken);

			var user = userService.GetActiveProfile(loginToken);
			var post = DataProvider.GetPostForEditing(postID);

			VerifyEditPermission(user, post);

			return post;
		}


		public void UpdatePost(Guid loginToken, EditPostModel post)
		{
			UpdateSession(loginToken);

			var user = userService.GetActiveProfile(loginToken);
			var originalPost = DataProvider.GetPostForEditing(post.PostID);

			VerifyEditPermission(user, originalPost);

			// last edit date is right now
			var lastEdit = DateTime.UtcNow;
			var lastEditUser = user.Details.UserID;

			DataProvider.UpdatePost(post, lastEdit, lastEditUser);
		}


		public BBTagReference[] GetForumCodes(Guid loginToken)
		{
			UpdateSession(loginToken);

			var codes = new List<BBTagReference>();
			foreach (var code in forumCodes)
			{
				code.RenderedSample = parser.ToHtml(code.Sample);
				codes.Add(code);
			}

			return codes.ToArray();
		}


		public int CreateReply(Guid loginToken, CreateReplyModel post)
		{
			UpdateSession(loginToken);

			var profile = userService.GetActiveProfile(loginToken);

			var allowed = profile.CanReply(post.ForumID);

			if (!allowed)
			{
				throw new NoAccessException();
			}

			// make sure the parent is not locked
			var parent = DataProvider.GetPostForViewing(post.ParentPostID);

			if (parent.Locked)
			{
				throw new NoAccessException();
			}

			// todo: validation on service layer
			var userID = DataProvider.GetProfile(loginToken).UserID;
			var postID = DataProvider.CreateReply(userID, post);

			return postID;
		}

		public void ChangeLockState(Guid loginToken, int postId, bool locked)
		{
			UpdateSession(loginToken);

			var profile = userService.GetActiveProfile(loginToken);

			var post = GetPostForViewing(loginToken, postId);

			var allowed = profile.CanModerate(post.ForumID);

			if (!allowed)
			{
				throw new NoAccessException();
			}

			DataProvider.ChangeLockState(postId, locked);

		}
	}
}
